/**
 * 渲染和提交
 * 1在 React 中渲染的含义是什么
 * 2为什么以及什么时候 React 会渲染一个组件
 * 3在屏幕上显示组件所涉及的步骤
 * 4为什么渲染并不一定会导致 DOM 更新
 */

/**
 * 步骤 1: 触发一次渲染
 *      1 初次触发渲染 --- root.render()
 *      2 状态更新时，重新触发渲染 --- 你就可以通过使用 set 函数 更新其状态来触发之后的渲染。更新组件的状态会自动将一次渲染送入队列
 *
 * 步骤 2: React 渲染你的组件
 *      在你触发渲染后，React 会调用你的组件来确定要在屏幕上显示的内容。“渲染中” 即 React 在调用你的组件。
 *      1 初次渲染 --- React 会调用根组件。
 *      2 对于后续的渲染, React 会调用内部状态更新触发了渲染的函数组件。
 *
 * 步骤 3: React 把更改提交到 DOM 上
 *      1 对于初次渲染，React 会使用 appendChild() DOM API 将其创建的所有 DOM 节点放在屏幕上。
 *      2 对于重新渲染，React 将应用最少的必要操作（在渲染时计算！），以使得 DOM 与最新的渲染输出相互匹配。
 */
import { useState } from 'react'
import dayjs from 'dayjs'
import { getFinalState } from '../utils/processQueue'

export function Image() {
  return (
    <img
      src="https://i.imgur.com/Mx7dA2Y.jpg"
      alt="'Floralis Genérica' by Eduardo Catalano: a gigantic metallic flower sculpture with reflective petals"
    />
  )
}

export function Clock({ time }) {
  //   if (document.querySelector('input')) {
  //     console.log(document.querySelector('input').value)
  //   }

  return (
    <>
      <h1>{time}</h1>
      <input />
    </>
  )
}

/**
 * state-as-a-snapshot
 */

/**
 * 初次渲染 --- React 将会为<section>、<h1> 和三个 <img> 标签 创建 DOM 节点。
 * 在一次重渲染过程中, React 将计算它们的哪些属性（如果有的话）自上次渲染以来已更改。在下一步（提交阶段）之前，它不会对这些信息执行任何操作。
 * @returns
 */
export function Gallery() {
  const [time] = useState(dayjs().format('hh:mm:ss')) //setTime
  //   setInterval(() => {
  //     setTime(dayjs().format('hh:mm:ss'))
  //   }, 1000)
  return (
    <section>
      <h1>鼓舞人心的雕塑</h1>
      <Image />
      <Image />
      <Image />
      <Clock time={time} />
    </section>
  )
}

export function Form() {
  const [isSent, setIsSent] = useState(false)
  const [message, setMessage] = useState('Hi!')
  if (isSent) {
    return <h1>Your message is on its way!</h1>
  }
  return (
    <form
      onSubmit={(e) => {
        e.preventDefault()
        setIsSent(true)
        sendMessage(message)
      }}
    >
      <textarea placeholder="Message" value={message} onChange={(e) => setMessage(e.target.value)} />
      <button type="submit">Send</button>
    </form>
  )
}

function sendMessage(message) {
  console.log(message)
}

export function TrafficLight() {
  const [walk, setWalk] = useState(true)

  function handleClick() {
    setWalk(!walk)
    alert(walk ? 'Stop is next' : 'Walk is next')
  }

  return (
    <>
      <h1
        style={{
          color: walk ? 'darkgreen' : 'darkred',
        }}
      >
        {walk ? 'Walk' : 'Stop'}
      </h1>
      <button onClick={handleClick}>Change to {walk ? 'Stop' : 'Walk'}</button>
    </>
  )
}

/**
 * 替代法: 你可以在心中替换事件处理函数中的 state，类似于替换渲染的 JSX。
 * 你可以用替代法来验证 这逻辑 是否正确。
 *  当 walk 为 true 时，你会得到：
 *      根据纯函数 得到输出jsx, 符合正常的逻辑
 *
 * 当正在通行时，
 *  1 红登h1显示 绿色的 walk
 *  2 切换到下一个状态，就是 setWalk(false) 并且提示'Stop is next'
 * @returns
 */
function TrafficLightWalkIsTrue() {
  /*eslint-disable-next-line */
  const [walk, setWalk] = useState(true)

  function handleClick() {
    setWalk(false)
    alert('Stop is next')
  }

  return (
    <>
      <h1 style={{ color: 'darkgreen' }}>Walk</h1>
      <button onClick={handleClick}>Change to Stop</button>
    </>
  )
}

/**
 * 批处理：
 *      事件处理函数多次改变state，不会触发多次渲染，只有事件处理函数执行完成后，才会更新UI
 *      如果事情处理函数 存在异步方法调用，会先更新 异步方法前 相关 UI?
 *
 * React 不会跨 多个 需要刻意触发的事件（如点击）进行批处理——每次点击都是单独处理的。请放心，React 只会在一般来说安全的情况下才进行批处理
 *     用户会担心 如果很快的点击按钮两次，是否也会进行 批处理
 *
 * 更新队列：队列是先进先出
 * setNumber(1) 对应更新队列 "替换为1"
 * setNumber(n => n + 1)：对应更新队列  n => n + 1
 */

function increment(n) {
  return n + 1
}
increment.toString = () => 'n => n+1'

function TestCase({ baseState, queue, expected }) {
  const actual = getFinalState(baseState, queue)
  return (
    <>
      <p>
        初始 state：<b>{baseState}</b>
      </p>
      <p>
        队列：<b>[{queue.join(', ')}]</b>
      </p>
      <p>
        预期结果：<b>{expected}</b>
      </p>
      <p style={{ color: actual === expected ? 'green' : 'red' }}>
        你的结果：<b>{actual}</b> ({actual === expected ? '正确' : '错误'})
      </p>
    </>
  )
}

export default function App() {
  return (
    <>
      <TestCase baseState={0} queue={[1, 1, 1]} expected={1} />
      <hr />
      <TestCase baseState={0} queue={[increment, increment, increment]} expected={3} />
      <hr />
      <TestCase baseState={0} queue={[5, increment]} expected={6} />
      <hr />
      <TestCase baseState={0} queue={[5, increment, 42]} expected={42} />
    </>
  )
}

const initialList = [
  { id: 0, title: 'Big Bellies' },
  { id: 1, title: 'Lunar Landscape' },
  { id: 2, title: 'Terracotta Army' },
]
/**
 * 虽然 nextList 和 list 是两个不同的数组，nextList[0] 和 list[0] 却指向了同一个对象。
 */
function StudentsArray() {
  const [list, setList] = useState(initialList)

  const nextList = [...list]
  nextList[0].seen = true // 问题：直接修改了 list[0] 的值
  setList(nextList)
}
